
#include <assert.h>
#include <algorithm>

#include "Universe.hpp"
#include "ValueNode.hpp"

Universe::Universe()
{

}

Universe::~Universe()
{

}

void Universe::solve()
{
    while (!this->_results.empty())
    {
        std::cout << "solving " << this->_results.front() << std::endl;
        this->_solve(this->_results.front());
        this->_results.pop_front();
        this->_childUniverses.clear();
    }
}

void Universe::_solve(char varname)
{
    if (this->_tree.find(varname) != this->_tree.end())
    {
        Tribool t = this->_tree[varname]->solve(*this);
        if (t.value != TriboolVal::OutOrStopped)
        {
            if (!this->_specificFacts.empty())
            {
                std::cout << "in universe with:" << std::endl;
                std::list<char>::iterator ite = this->_specificFacts.end();
                for (std::list<char>::iterator it = this->_specificFacts.begin(); it != ite; it++)
                    std::cout << "\t[" << *it << " = " << this->_facts[*it] << "]" << std::endl;
            }
            std::cout << "\t" << varname << " is " << t << std::endl;
        }
        else
        {
            std::cout << "Multi universes have been created cause of 'OR' or 'XOR' in conclusion." << std::endl;
            std::list<Universe>::iterator ite = this->_childUniverses.end();
            for (std::list<Universe>::iterator it = this->_childUniverses.begin(); it != ite; it++)
                it->_solve(varname);
        }
    }
    else
        std::cout << "Cannot solve " << varname << " cause it doesn't appear in given data." << std::endl;
}

void Universe::duplicate()
{
    Universe clone;
    std::list<char>::iterator r_ite = this->_results.end();
    for (std::list<char>::iterator r_it = this->_results.begin(); r_it != r_ite; r_it++)
        clone._results.push_back(*r_it);
    std::list<char>::iterator sf_ite = this->_specificFacts.end();
    for (std::list<char>::iterator sf_it = this->_specificFacts.begin(); sf_it != sf_ite; sf_it++)
        clone._results.push_back(*sf_it);
    std::map<char, Tribool>::iterator f_ite = this->_facts.end();
    for (std::map<char, Tribool>::iterator f_it = this->_facts.begin(); f_it != f_ite; f_it++)
        clone.addFact(f_it->first, f_it->second);
    std::map<char, ExpressionNode*>::iterator t_ite = this->_tree.end();
    for (std::map<char, ExpressionNode*>::iterator t_it = this->_tree.begin(); t_it != t_ite; t_it++)
        clone._tree[t_it->first] = t_it->second->clone(clone._allNodes);
    this->_childUniverses.push_back(clone);
}

ExpressionNode* Universe::addValueNode(char name)
{
    if (this->_tree.find(name) == this->_tree.end())
        this->_tree[name] = new ValueNode(name);
    return this->_tree[name];
}

ExpressionNode* Universe::getValueNode(char name)
{
    if (this->_tree.find(name) != this->_tree.end())
        return this->_tree[name];
    return 0;
}

bool Universe::isFact(char name)
{
    return (this->_facts.find(name) != this->_facts.end());
}

Tribool Universe::getFact(char name)
{
    if (this->_facts.find(name) != this->_facts.end())
        return this->_facts[name];
    return TriboolVal::Undefined;
}

void Universe::addFact(char name, Tribool value)
{
    this->_facts[name] = value;
}

void Universe::addResult(char var)
{
    this->_results.push_back(var);
}

Universe& Universe::getLastChild()
{
    assert(!this->_childUniverses.empty());
    return this->_childUniverses.back();
}

ExpressionNode* Universe::getNodeById(int id)
{
    if (this->_allNodes.find(id) != this->_allNodes.end())
        return this->_allNodes[id];
    return 0;
}

void Universe::addSpecificFacts(char name)
{
    std::list<char>::iterator ite = this->_specificFacts.end();
    for (std::list<char>::iterator it = this->_specificFacts.begin(); it != ite; it++)
        if (*it == name)
            return;
    this->_specificFacts.push_back(name);
}
